package goreplica

import (
	"gozk"
	"os"
	"strconv"
)

type Replica struct {
	zkHost *gozk.ZooKeeper
	path string
	state int
}

func New_Replica(host string, p string) *Replica {
	path := p
	return &Replica{nil, path, 0}
}

func (rep *Replica) Synchronize() os.Error {
	for {
		str, _, w, err := rep.zkHost.GetW("/state")
		if err != nil {
			return os.NewError(err.String())
		}
		newState, e := strconv.Atoi(str)
		if err != nil {
			return e;
		}
		if newState != rep.state {
			e := rep.update()
			if e != nil {
				return e
			}
		}
		
		event := <-w
		if event.Type != gozk.EVENT_CHANGED {
	    	return os.NewError("state znode error")
    	}
	}
    
    defer os.NewError("Couldn't Synchronize")
    return os.NewError("Couldn't Synchronize")
}

func (rep *Replica) update() os.Error {
	childs, _, err := rep.zkHost.GetChildren(rep.path + "/data")
	if err != nil {
		return os.NewError(err.String())
	}
	
	for _,s := range childs {
		info, _, err := rep.zkHost.Get(rep.path + "/data/" + s)
		if err != nil {
			return os.NewError(err.String())
		}
		if info != "removed" {
			node, e := strconv.Atoi(s)
			if e != nil {
				return e
			}
			if node > rep.state {
				data, _, err := rep.zkHost.Get(rep.path + "/data/" + s + "/info")
				if err != nil {
					return os.NewError(err.String())
				}
				//TODO add the new info to the server
				println(data)
			}
		} else {
			//TODO Check check the name server and remove if necessary
			println("Removed")
		}
	}
	return nil
}


func (rep *Replica) Upload(newData string) os.Error {
	old, _, err := rep.zkHost.Get(rep.path + "/state")
	if err != nil {
		return os.NewError(err.String())
	}
	
	new, e := strconv.Atoi(old)
	if e != nil {
		return e
	}
	new += 1
	
	st, err := rep.zkHost.Set(rep.path + "/state", strconv.Itoa(new), 0)
	if err != nil {
		return os.NewError(err.String())
	}
	if st != nil {
		return os.NewError(err.String())
	}

	_, err = rep.zkHost.Create(rep.path + "/data/" + strconv.Itoa(new) + "valor", newData, 0, gozk.WorldACL(gozk.PERM_ALL))
	if err != nil {
		return os.NewError(err.String())
	}
	
	return nil
}

func (rep *Replica) Remove(someData string) os.Error {
	childs, _, err := rep.zkHost.GetChildren(rep.path + "/data")
	if err != nil {
		return os.NewError(err.String())
	}
	
	for _,s := range childs {
		data, _, err := rep.zkHost.Get(rep.path + "/data/" + s + "/info")
		if err != nil {
			return os.NewError(err.String())
		}
		if data == someData {
			st, err := rep.zkHost.Set(rep.path + "/data/" + s, "removed", 0)
			if err != nil {
				return os.NewError(err.String())
			}
			if(st != nil) {
				return os.NewError(err.String())
			}
		}
	}
	
	old, _, err := rep.zkHost.Get(rep.path + "/state")
	if err != nil {
		return os.NewError(err.String())
	}
	
	st, err := rep.zkHost.Set(rep.path + "/state", old, 0)
	if err != nil {
		return os.NewError(err.String())
	}
	if st != nil {
		return os.NewError(err.String())
	}
	
	return nil
}

func (rep *Replica) Init(host string) os.Error {
		x, session, err := gozk.Init(host, 5000)
		if err != nil {
		    return os.NewError(err.String())
		}

		// Wait for connection.
		event := <-session
		if event.State != gozk.STATE_CONNECTED {
		    return os.NewError("Couldn't connect")
		}
		
		rep.zkHost = x
		
		sts, err := rep.zkHost.Exists(rep.path + "/data")
		if err != nil {
			return os.NewError(err.String())
		}
		if sts != nil {
			_, err = rep.zkHost.Create(rep.path + "/data", "0", 0, gozk.WorldACL(gozk.PERM_ALL))
			if err != nil {
				return os.NewError(err.String())
			}
		}
		
		sts, err = rep.zkHost.Exists(rep.path + "/state")
		if err != nil {
			return os.NewError(err.String())
		}
		if sts != nil {
			//TODO upload of the data?.
			_, err = rep.zkHost.Create(rep.path + "/state", "0", 0, gozk.WorldACL(gozk.PERM_ALL))
			if err != nil {
				return os.NewError(err.String())
			}
		} else {
			state, _, err := rep.zkHost.Get(rep.path + "/state")
			if err != nil {
				return os.NewError(err.String())
			}
			e := rep.update()
			if e != nil {
				return e
			}
			newState, e := strconv.Atoi(state)
			if e != nil {
				return e
			}
			rep.state = newState
		}
		
		return nil
}

func (rep *Replica) Close() {
	rep.zkHost.Close()
}
